#pragma once

#include <any>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <queue>
#include <sstream>
#include <thread>

namespace AsyncLog {
enum LogLevel {
    DEBUG = 0,
    INFO = 1,
    WARN = 2,
    ERROR = 3,
};

class LogTask {
public:
    LogTask() {}
    LogTask(const LogTask& src)
        : _level(src._level), _logDatas(src._logDatas) {}
    LogTask(const LogTask&& src)
        : _level(src._level), _logDatas(std::move(src._logDatas)) {}
    LogLevel _level;
    std::queue<std::any> _logDatas;
};

class AsyncLog {
public:
    static AsyncLog& Instance() {
        static AsyncLog instance;
        return instance;
    }

    ~AsyncLog() {
        Stop();
        workThread.join();
        std::cout << "exit success" << std::endl;
    }

    template <typename T>
    std::any toAny(const T& value) {
        return std::any(value);
    }

    // 可变参数列表，异步写
    template <typename... Args>
    void AsyncWrite(LogLevel level, Args&&... args) {
        auto task = std::make_shared<LogTask>();
        // 折叠表达式依次将可变参数写入队列，需C++17版本支持
        // 折叠表达式基本语法：(表达式,
        // ...)，会循环执行表达式，直到参数列表用完。
        (task->_logDatas.push(args), ...);
        task->_level = level;
        std::unique_lock<std::mutex> lock(_mtx);
        _queue.push(task);
        bool notify = (_queue.size() == 1) ? true : false;
        lock.unlock();
        // 通知等待的线程有新的任务可处理
        if (notify) {
            _empty_cond.notify_one();
        }
    }

    void Stop() {
        _b_stop = true;
        _empty_cond.notify_one();
    }

private:
    AsyncLog() : _b_stop(false) {
        workThread = std::thread([this]() {
            for (;;) {
                std::unique_lock<std::mutex> lock(_mtx);
                while (_queue.empty() && !_b_stop) {
                    _empty_cond.wait(lock);
                }
                if (_b_stop) {
                    return;
                }
                auto logtask = _queue.front();
                _queue.pop();
                lock.unlock();
                processTask(logtask);
            }
        });
    }

    AsyncLog& operator=(const AsyncLog&) = delete;
    AsyncLog(const AsyncLog&) = delete;

    bool convert2Str(const std::any& data, std::string& str) {
        std::ostringstream ss;
        if (data.type() == typeid(int)) {
            ss << std::any_cast<int>(data);
        } else if (data.type() == typeid(float)) {
            ss << std::any_cast<float>(data);
        } else if (data.type() == typeid(double)) {
            ss << std::any_cast<double>(data);
        } else if (data.type() == typeid(std::string)) {
            ss << std::any_cast<std::string>(data);
        } else if (data.type() == typeid(char*)) {
            ss << std::any_cast<char*>(data);
        } else if (data.type() == typeid(char const*)) {
            ss << std::any_cast<char const*>(data);
        } else {
            return false;
        }
        str = ss.str();
        return true;
    }

    void processTask(std::shared_ptr<LogTask> task) {
        std::cout << "Log level is " << task->_level << std::endl;

        if (task->_logDatas.empty()) {
            return;
        }

        // 队列首元素
        auto head = task->_logDatas.front();
        task->_logDatas.pop();

        std::string formatStr = "";
        bool bsuccess = convert2Str(head, formatStr);
        if (!bsuccess) {
            return;
        }

        for (; !(task->_logDatas.empty());) {
            auto data = task->_logDatas.front();
            formatStr = formatString(formatStr, data);
            task->_logDatas.pop();
        }

        std::cout << "Log string is " << formatStr << std::endl;
    }

    template <typename... Args>
    std::string formatString(const std::string& format, Args... args) {
        std::string result = format;
        size_t pos = 0;
        // lambda表达式查找并替换字符串
        auto replacePlaceholder = [&](const std::string& placeholder,
                                      const std::any& replacement) {
            std::string str_replement = "";
            bool bsuccess = convert2Str(replacement, str_replement);
            if (!bsuccess) {
                return;
            }

            size_t placeholderPos = result.find(placeholder, pos);
            if (placeholderPos != std::string::npos) {
                result.replace(
                    placeholderPos, placeholder.length(), str_replement);
                pos = placeholderPos + str_replement.length();
            } else {
                result = result + " " + str_replement;
            }
        };

        (replacePlaceholder("{}", args), ...);
        return result;
    }

    std::condition_variable _empty_cond;
    std::queue<std::shared_ptr<LogTask>> _queue;
    bool _b_stop;
    std::mutex _mtx;
    std::thread workThread;
};

template <typename... Args>
void DLog(Args&&... args) {
    AsyncLog::Instance().AsyncWrite(DEBUG, std::forward<Args>(args)...);
}

template <typename... Args>
void ILog(Args&&... args) {
    AsyncLog::Instance().AsyncWrite(INFO, std::forward<Args>(args)...);
}

template <typename... Args>
void WLog(Args&&... args) {
    AsyncLog::Instance().AsyncWrite(WARN, std::forward<Args>(args)...);
}

template <typename... Args>
void ELog(Args&&... args) {
    AsyncLog::Instance().AsyncWrite(ERROR, std::forward<Args>(args)...);
}

}  // namespace AsyncLog